// package middlewares 实现了项目中用到的所有中间件
package middlewares

import (
	"errors"
	"gforum/global"
	"gforum/model"
	"gforum/utils"
	"github.com/gin-gonic/gin"
	"strings"
)

// AuthRequired 校验 http request 中是否有合法的 Authorization token
func AuthRequired() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		token := ctx.GetHeader("Authorization")
		if token == "" {
			utils.Unauthorized(ctx, errors.New("missing auth token"), "Token is required")
			ctx.Abort()
			return
		}

		username, err := utils.ParseJWT(token)
		if err != nil {
			utils.Unauthorized(ctx, err, "Invalid auth token")
			ctx.Abort()
			return
		}

		ctx.Set("username", username)
		ctx.Next()
	}
}

// PostPostAllowed 校验用户是否有发布贴子的权限
//
// 从 gin.Context 获取进行发布帖子的用户的用户名，从数据库搜索该用户等级是否为 2 以上。
// 用于 POST api/posts
func PostPostAllowed() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		username := ctx.MustGet("username").(string)

		var user model.User
		result := global.Db.Where("username = ?", username).First(&user)
		if result.Error != nil {
			utils.InternalServerError(ctx, errors.New("user not found"), "Internal error")
			ctx.Abort()
			return
		}
		if result.RowsAffected == 0 {
			utils.BadRequest(ctx, errors.New("user not found"), "BadRequest")
			ctx.Abort()
			return
		}

		var postContent model.PostContent
		if err := ctx.ShouldBindJSON(&postContent); err != nil {
			utils.BadRequest(ctx, err, "Invalid post content")
			ctx.Abort()
			return
		}

		if postContent.AuthorID != user.ID {
			utils.Unauthorized(ctx, errors.New("authorID should same with user's ID"), "Unauthorized")
			ctx.Abort()
			return
		}

		err := global.Db.Model(&model.User{}).Where("username = ? AND level > 1", username).Error
		if err != nil {
			utils.Unauthorized(ctx, errors.New("user are not allowed to post Post"), "level should high than 1")
			ctx.Abort()
			return
		}

		ctx.Set("postContent", postContent)
		ctx.Next()
	}
}

// PostCommentAllowed 校验用户是否有发布评论的权限
//
// 行为类似 PostPostAllowed, 但是不校验用户等级，因为 1 级就能评论
func PostCommentAllowed() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		username := ctx.MustGet("username").(string)

		var user model.User
		result := global.Db.Where("username = ?", username).First(&user)
		if result.Error != nil {
			utils.InternalServerError(ctx, errors.New("user not found"), "Internal error")
			ctx.Abort()
			return
		}
		if result.RowsAffected == 0 {
			utils.BadRequest(ctx, errors.New("user not found"), "BadRequest")
			ctx.Abort()
			return
		}

		var commentContent model.Comment
		if err := ctx.ShouldBindJSON(&commentContent); err != nil {
			utils.BadRequest(ctx, err, "Invalid comment content")
			ctx.Abort()
			return
		}

		if commentContent.UserID != user.ID {
			utils.Unauthorized(ctx, errors.New("userID should same with user's ID"), "Unauthorized")
			ctx.Abort()
			return
		}

		ctx.Set("commentContent", commentContent)
		ctx.Next()
	}
}

// EditPermissionRequired 校验用户是否具备编辑(删除)权限, 管理员和等级大于等于 2 的资源所有者有权限
//
// 从 gin.Context 获取进行修改的用户名，从数据库搜索该用户是否为需要删除的资源的作者或管理员。
// 从 http.Request 中获取请求需要编辑的数据的类型，并依此通过 Comment.UserID 或者 Post.AuthorID 获取校验用户权限
func EditPermissionRequired() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		username := ctx.MustGet("username").(string)
		var user model.User
		result := global.Db.Where("username = ?", username).First(&user)
		if result.RowsAffected == 0 {
			utils.Unauthorized(ctx, errors.New("user not found"), "user not found")
			ctx.Abort()
			return
		}

		id := ctx.Param("id")

		requestURL := strings.Trim(ctx.Request.URL.String(), "/")
		modelStr := strings.Split(requestURL, "/")[1]

		// 校验用户是否为资源的发布者且等级
		isOwner := false
		switch modelStr {
		case "posts":
			var post model.Post
			result = global.Db.Model(&model.Post{}).Where("id = ? AND author_id = ?", id, user.ID).First(&post)
			if result.RowsAffected != 0 && user.Level >= 2 {
				isOwner = true
			}
		case "comments":
			var comment model.Comment
			result = global.Db.Model(&model.Comment{}).Where("id = ? AND user_id = ?", id, user.ID).First(&comment)
			if result.RowsAffected != 0 && user.Level >= 2 {
				isOwner = true
			}
		}

		// 如果该用户非发布者或没权限, 则判断是否为 super user
		isSu := false
		result = global.Db.Model(&model.User{}).Where("username = ? AND level = 4", username)
		if result.RowsAffected != 0 {
			isSu = true
		}

		if !isSu && !isOwner {
			utils.Unauthorized(ctx, errors.New("user not allowed to edit post"), "user not allowed to edit post")
			ctx.Abort()
			return
		}

		ctx.Next()
	}
}
